using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

#if UNITY_EDITOR || BURST_INTERNAL
namespace Unity.Burst.Editor
{
    internal partial class BurstDisassembler
    {
        internal class ARM64AsmTokenKindProvider : AsmTokenKindProvider
        {
            private static readonly string[] Registers = new[]
            {
                "nzcv",
                "wsp",
                "sp",
                "lr",

                "r0",
                "r1",
                "r2",
                "r3",
                "r4",
                "r5",
                "r6",
                "r7",
                "r8",
                "r9",
                "r10",
                "r11",
                "r12",
                "r13",
                "r14",
                "r15",

                "b0",
                "b1",
                "b2",
                "b3",
                "b4",
                "b5",
                "b6",
                "b7",
                "b8",
                "b9",
                "b10",
                "b11",
                "b12",
                "b13",
                "b14",
                "b15",
                "b16",
                "b17",
                "b18",
                "b19",
                "b20",
                "b21",
                "b22",
                "b23",
                "b24",
                "b25",
                "b26",
                "b27",
                "b28",
                "b29",
                "b30",
                "b31",
                "d0",
                "d1",
                "d2",
                "d3",
                "d4",
                "d5",
                "d6",
                "d7",
                "d8",
                "d9",
                "d10",
                "d11",
                "d12",
                "d13",
                "d14",
                "d15",
                "d16",
                "d17",
                "d18",
                "d19",
                "d20",
                "d21",
                "d22",
                "d23",
                "d24",
                "d25",
                "d26",
                "d27",
                "d28",
                "d29",
                "d30",
                "d31",
                "h0",
                "h1",
                "h2",
                "h3",
                "h4",
                "h5",
                "h6",
                "h7",
                "h8",
                "h9",
                "h10",
                "h11",
                "h12",
                "h13",
                "h14",
                "h15",
                "h16",
                "h17",
                "h18",
                "h19",
                "h20",
                "h21",
                "h22",
                "h23",
                "h24",
                "h25",
                "h26",
                "h27",
                "h28",
                "h29",
                "h30",
                "h31",
                "q0",
                "q1",
                "q2",
                "q3",
                "q4",
                "q5",
                "q6",
                "q7",
                "q8",
                "q9",
                "q10",
                "q11",
                "q12",
                "q13",
                "q14",
                "q15",
                "q16",
                "q17",
                "q18",
                "q19",
                "q20",
                "q21",
                "q22",
                "q23",
                "q24",
                "q25",
                "q26",
                "q27",
                "q28",
                "q29",
                "q30",
                "q31",
                "s0",
                "s1",
                "s2",
                "s3",
                "s4",
                "s5",
                "s6",
                "s7",
                "s8",
                "s9",
                "s10",
                "s11",
                "s12",
                "s13",
                "s14",
                "s15",
                "s16",
                "s17",
                "s18",
                "s19",
                "s20",
                "s21",
                "s22",
                "s23",
                "s24",
                "s25",
                "s26",
                "s27",
                "s28",
                "s29",
                "s30",
                "s31",
                "w0",
                "w1",
                "w2",
                "w3",
                "w4",
                "w5",
                "w6",
                "w7",
                "w8",
                "w9",
                "w10",
                "w11",
                "w12",
                "w13",
                "w14",
                "w15",
                "w16",
                "w17",
                "w18",
                "w19",
                "w20",
                "w21",
                "w22",
                "w23",
                "w24",
                "w25",
                "w26",
                "w27",
                "w28",
                "w29",
                "w30",
                "wzr",
                "x0",
                "x1",
                "x2",
                "x3",
                "x4",
                "x5",
                "x6",
                "x7",
                "x8",
                "x9",
                "x10",
                "x11",
                "x12",
                "x13",
                "x14",
                "x15",
                "x16",
                "x17",
                "x18",
                "x19",
                "x20",
                "x21",
                "x22",
                "x23",
                "x24",
                "x25",
                "x26",
                "x27",
                "x28",
                "x29",
                "x30",

                "v0",
                "v1",
                "v2",
                "v3",
                "v4",
                "v5",
                "v6",
                "v7",
                "v8",
                "v9",
                "v10",
                "v11",
                "v12",
                "v13",
                "v14",
                "v15",
                "v16",
                "v17",
                "v18",
                "v19",
                "v20",
                "v21",
                "v22",
                "v23",
                "v24",
                "v25",
                "v26",
                "v27",
                "v28",
                "v29",
                "v30",
            };

            private static readonly string[] Instructions = new[]
            {
                "bkpt",
                "usat",
                "stm",
                "stmia",
                "stmib",
                "stmda",
                "stmdb",
                "ldm",
                "ldmia",
                "ldmib",
                "ldmda",
                "ldmdb",
                "it",
                "itt",
                "ittt",
                "itttt",
                "ite",
                "itte",
                "ittte",
                "itee",
                "ittee",
                "uxtab",
                "uxtah",
                "abs",
                "adc",
                "add",
                "addhi",
                "adds",
                "addhn",
                "addhn2",
                "addp",
                "addv",
                "addw",
                "adr",
                "adrp",
                "aesd",
                "aese",
                "aesimc",
                "aesmc",
                "and",
                "asr",
                "asrs",
                "at",
                "bfi",
                "bfm",
                "bfxil",
                "bic",
                "bif",
                "bit",
                "brk",
                "bsl",
                "ccmn",
                "ccmp",
                "clrex",
                "cls",
                "clz",
                "cmeq",
                "cmge",
                "cmgt",
                "cmhi",
                "cmhs",
                "cmle",
                "cmlt",
                "cmn",
                "cmp",
                "cmtst",
                "cnt",
                "crc32b",
                "crc32cb",
                "crc32ch",
                "crc32cw",
                "crc32cx",
                "crc32h",
                "crc32w",
                "crc32x",
                "csel",
                "csinc",
                "csinv",
                "csneg",
                "dc",
                "dcps1",
                "dcps2",
                "dcps3",
                "dmb",
                "drps",
                "dsb",
                "dup",
                "eon",
                "eor",
                "eors",
                "eret",
                "ext",
                "extr",
                "fabd",
                "fabs",
                "facge",
                "facgt",
                "fadd",
                "faddp",
                "fccmp",
                "fccmpe",
                "fcmeq",
                "fcmge",
                "fcmgt",
                "fcmle",
                "fcmlt",
                "fcmp",
                "fcmpe",
                "fcsel",
                "fcvt",
                "fcvtas",
                "fcvtau",
                "fcvtl",
                "fcvtl2",
                "fcvtms",
                "fcvtmu",
                "fcvtn",
                "fcvtn2",
                "fcvtns",
                "fcvtnu",
                "fcvtps",
                "fcvtpu",
                "fcvtxn",
                "fcvtxn2",
                "fcvtzs",
                "fcvtzu",
                "fdiv",
                "fmadd",
                "fmax",
                "fmaxnm",
                "fmaxnmp",
                "fmaxnmv",
                "fmaxp",
                "fmaxv",
                "fmin",
                "fminnm",
                "fminnmp",
                "fminnmv",
                "fminp",
                "fminv",
                "fmla",
                "fmls",
                "fmov",
                "fmsub",
                "fmul",
                "fmulx",
                "fneg",
                "fnmadd",
                "fnmsub",
                "fnmul",
                "frecpe",
                "frecps",
                "frecpx",
                "frinta",
                "frinti",
                "frintm",
                "frintn",
                "frintp",
                "frintx",
                "frintz",
                "frsqrte",
                "frsqrts",
                "fsqrt",
                "fsub",
                "hint",
                "hlt",
                "hvc",
                "ic",
                "ins",
                "isb",
                "ld1",
                "ld1r",
                "ld2",
                "ld2r",
                "ld3",
                "ld3r",
                "ld4",
                "ld4r",
                "ldar",
                "ldarb",
                "ldarh",
                "ldaxp",
                "ldaxr",
                "ldaxrb",
                "ldaxrh",
                "ldnp",
                "ldp",
                "ldpsw",
                "ldr",
                "ldrb",
                "ldrd",
                "ldrh",
                "ldrsb",
                "ldrsh",
                "ldrsw",
                "ldtr",
                "ldtrb",
                "ldtrh",
                "ldtrsb",
                "ldtrsh",
                "ldtrsw",
                "ldur",
                "ldurb",
                "ldurh",
                "ldursb",
                "ldursh",
                "ldursw",
                "ldxp",
                "ldxr",
                "ldxrb",
                "ldxrh",
                "lsl",
                "lsls",
                "lsr",
                "lsrs",
                "umaal",
                "smmul",
                "madd",
                "mla",
                "mls",
                "mneg",
                "mov",
                "movi",
                "movk",
                "movn",
                "movt",
                "movz",
                "movw",
                "mrs",
                "msr",
                "msub",
                "mul",
                "muls",
                "mvn",
                "mvns",
                "mvni",
                "neg",
                "ngc",
                "nop",
                "not",
                "orn",
                "orr",
                "orrs",
                "pmul",
                "pmull",
                "pmull2",
                "prfm",
                "prfum",
                "raddhn",
                "raddhn2",
                "rbit",
                "rev",
                "rev16",
                "rev32",
                "rev64",
                "rrx",
                "ror",
                "rshrn",
                "rshrn2",
                "rsubhn",
                "rsubhn2",
                "saba",
                "sabal",
                "sabal2",
                "sabd",
                "sabdl",
                "sabdl2",
                "sadalp",
                "saddl",
                "saddl2",
                "saddlp",
                "saddlv",
                "saddw",
                "saddw2",
                "sbc",
                "sbfiz",
                "sbfm",
                "sbfx",
                "scvtf",
                "sdiv",
                "sev",
                "sevl",
                "sha1c",
                "sha1h",
                "sha1m",
                "sha1p",
                "sha1su0",
                "sha1su1",
                "sha256h",
                "sha256h2",
                "sha256su0",
                "sha256su1",
                "shadd",
                "shl",
                "shll",
                "shll2",
                "shrn",
                "shrn2",
                "shsub",
                "sli",
                "smaddl",
                "smax",
                "smaxp",
                "smaxv",
                "smc",
                "smin",
                "sminp",
                "sminv",
                "smlabb",
                "smlabt",
                "smlatb",
                "smlatt",
                "smlal",
                "smlal2",
                "smlsl",
                "smlsl2",
                "smnegl",
                "smov",
                "smsubl",
                "smulh",
                "smull",
                "smull2",
                "sqabs",
                "sqadd",
                "sqdmlal",
                "sqdmlal2",
                "sqdmlsl",
                "sqdmlsl2",
                "sqdmulh",
                "sqdmull",
                "sqdmull2",
                "sqneg",
                "sqrdmulh",
                "sqrshl",
                "sqrshrn",
                "sqrshrn2",
                "sqrshrun",
                "sqrshrun2",
                "sqshl",
                "sqshlu",
                "sqshrn",
                "sqshrn2",
                "sqshrun",
                "sqshrun2",
                "sqsub",
                "sqxtn",
                "sqxtn2",
                "sqxtun",
                "sqxtun2",
                "srhadd",
                "sri",
                "srshl",
                "srshr",
                "srsra",
                "sshl",
                "sshll",
                "sshll2",
                "sshr",
                "ssra",
                "ssubl",
                "ssubl2",
                "ssubw",
                "ssubw2",
                "st1",
                "st2",
                "st3",
                "st4",
                "stlr",
                "stlrb",
                "stlrh",
                "stlxp",
                "stlxr",
                "stlxrb",
                "stlxrh",
                "stnp",
                "stp",
                "str",
                "strd",
                "strb",
                "strh",
                "sttr",
                "sttrb",
                "sttrh",
                "stur",
                "sturb",
                "sturh",
                "stxp",
                "stxr",
                "stxrb",
                "stxrh",
                "sub",
                "subw",
                "subs",
                "rsb",
                "rsbs",
                "subhn",
                "subhn2",
                "suqadd",
                "svc",
                "sxtb",
                "sxth",
                "sxtw",
                "sys",
                "sysl",
                "tbl",
                "tbx",
                "tbb",
                "tbh",
                "tlbi",
                "trn1",
                "trn2",
                "tst",
                "uaba",
                "uabal",
                "uabal2",
                "uabd",
                "uabdl",
                "uabdl2",
                "uadalp",
                "uaddl",
                "uaddl2",
                "uaddlp",
                "uaddlv",
                "uaddw",
                "uaddw2",
                "ubfiz",
                "ubfm",
                "ubfx",
                "ucvtf",
                "udiv",
                "uhadd",
                "uhsub",
                "umaddl",
                "umax",
                "umaxp",
                "umaxv",
                "umin",
                "uminp",
                "uminv",
                "umlal",
                "umlal2",
                "umlsl",
                "umlsl2",
                "umnegl",
                "umov",
                "umsubl",
                "umulh",
                "umull",
                "umull2",
                "uqadd",
                "uqrshl",
                "uqrshrn",
                "uqrshrn2",
                "uqshl",
                "uqshrn",
                "uqshrn2",
                "uqsub",
                "uqxtn",
                "uqxtn2",
                "urecpe",
                "urhadd",
                "urshl",
                "urshr",
                "ursqrte",
                "ursra",
                "ushl",
                "ushll",
                "ushll2",
                "ushr",
                "usqadd",
                "usra",
                "usubl",
                "usubl2",
                "usubw",
                "usubw2",
                "uxtb",
                "uxth",
                "uzp1",
                "uzp2",
                "wfe",
                "wfi",
                "xtn",
                "xtn2",
                "yield",
                "zip1",
                "zip2",
                "adcs",
                "addg",
                "adrl",
                "ands",
                "asrv",
                "autda",
                "autdza",
                "autdb",
                "autdzb",
                "autia",
                "autiza",
                "autia1716",
                "autiasp",
                "autiaz",
                "autib",
                "autizb",
                "autib1716",
                "autibsp",
                "autibz",
                "axflag",
                "b.cond",
                "bfc",
                "bics",
                "cinc",
                "cinv",
                "cmpp",
                "cneg",
                "csdb",
                "cset",
                "csetm",
                "eretaa",
                "eretab",
                "esb",
                "irg",
                "ldg",
                "ldgv",
                "lslv",
                "lsrv",
                "movl",
                "negs",
                "ngcs",
                "pacda",
                "pacdza",
                "pacdb",
                "pacdzb",
                "pacga",
                "pacia",
                "paciza",
                "pacia1716",
                "paciasp",
                "paciaz",
                "pacib",
                "pacizb",
                "pacib1716",
                "pacibsp",
                "pacibz",
                "psb",
                "rorv",
                "sbcs",
                "st2g",
                "stg",
                "stgp",
                "stgv",
                "stz2g",
                "stzg",
                "subg",
                "subp",
                "subps",
                "xaflag",
                "xpacd",
                "xpaci",
                "xpaclri",
                "casa",
                "casal",
                "cas",
                "casl",
                "casab",
                "casalb",
                "casb",
                "caslb",
                "casah",
                "casalh",
                "cash",
                "caslh",
                "caspa",
                "caspal",
                "casp",
                "caspl",
                "ldadda",
                "ldaddal",
                "ldadd",
                "ldaddl",
                "ldaddab",
                "ldaddalb",
                "ldaddb",
                "ldaddlb",
                "ldaddah",
                "ldaddalh",
                "ldaddh",
                "ldaddlh",
                "ldapr",
                "ldaprb",
                "ldaprh",
                "ldclra",
                "ldclral",
                "ldclr",
                "ldclrl",
                "ldclrab",
                "ldclralb",
                "ldclrb",
                "ldclrlb",
                "ldclrah",
                "ldclralh",
                "ldclrh",
                "ldclrlh",
                "ldeora",
                "ldeoral",
                "ldeor",
                "ldeorl",
                "ldeorab",
                "ldeoralb",
                "ldeorb",
                "ldeorlb",
                "ldeorah",
                "ldeoralh",
                "ldeorh",
                "ldeorlh",
                "ldlar",
                "ldlarb",
                "ldlarh",
                "ldraa",
                "ldrab",
                "ldrex",
                "strex",
                "ldseta",
                "ldsetal",
                "ldset",
                "ldsetl",
                "ldsetab",
                "ldsetalb",
                "ldsetb",
                "ldsetlb",
                "ldsetah",
                "ldsetalh",
                "ldseth",
                "ldsetlh",
                "ldsmaxa",
                "ldsmaxal",
                "ldsmax",
                "ldsmaxl",
                "ldsmaxab",
                "ldsmaxalb",
                "ldsmaxb",
                "ldsmaxlb",
                "ldsmaxah",
                "ldsmaxalh",
                "ldsmaxh",
                "ldsmaxlh",
                "ldsmina",
                "ldsminal",
                "ldsmin",
                "ldsminl",
                "ldsminab",
                "ldsminalb",
                "ldsminb",
                "ldsminlb",
                "ldsminah",
                "ldsminalh",
                "ldsminh",
                "ldsminlh",
                "ldumaxa",
                "ldumaxal",
                "ldumax",
                "ldumaxl",
                "ldumaxab",
                "ldumaxalb",
                "ldumaxb",
                "ldumaxlb",
                "ldumaxah",
                "ldumaxalh",
                "ldumaxh",
                "ldumaxlh",
                "ldumina",
                "lduminal",
                "ldumin",
                "lduminl",
                "lduminab",
                "lduminalb",
                "lduminb",
                "lduminlb",
                "lduminah",
                "lduminalh",
                "lduminh",
                "lduminlh",
                "stadd",
                "staddl",
                "staddb",
                "staddlb",
                "staddh",
                "staddlh",
                "stclr",
                "stclrl",
                "stclrb",
                "stclrlb",
                "stclrh",
                "stclrlh",
                "steor",
                "steorl",
                "steorb",
                "steorlb",
                "steorh",
                "steorlh",
                "stllr",
                "stllrb",
                "stllrh",
                "stset",
                "stsetl",
                "stsetb",
                "stsetlb",
                "stseth",
                "stsetlh",
                "stsmax",
                "stsmaxl",
                "stsmaxb",
                "stsmaxlb",
                "stsmaxh",
                "stsmaxlh",
                "stsmin",
                "stsminl",
                "stsminb",
                "stsminlb",
                "stsminh",
                "stsminlh",
                "stumax",
                "stumaxl",
                "stumaxb",
                "stumaxlb",
                "stumaxh",
                "stumaxlh",
                "stumin",
                "stuminl",
                "stuminb",
                "stuminlb",
                "stuminh",
                "stuminlh",
                "swpa",
                "swpal",
                "swp",
                "swpl",
                "swpab",
                "swpalb",
                "swpb",
                "swplb",
                "swpah",
                "swpalh",
                "swph",
                "swplh",
                "fjcvtzs",
                "fcmla",
                "fmlal",
                "fmlsl",
                "sqrdmlah",
                "sqrdmlsh",
                "fcadd",
                "sdot",
                "sxtl",
                "sxtl2",
                "udot",
                "uxtl",
                "uxtl2",
                "bcax",
                "eor3",
                "rax1",
                "sha512h2",
                "sha512h",
                "sha512su0",
                "sha512su1",
                "sm3partw1",
                "sm3partw2",
                "sm3ss1",
                "sm3tt1a",
                "sm3tt1b",
                "sm3tt2a",
                "sm3tt2b",
                "sm4e",
                "sm4ekey",
                "xar",
            };

            private static readonly string[] BranchInstructions = new string[]
            {
                "beq",
                "bne",
                "bcs",
                "bhs",
                "bcc",
                "blo",
                "bmi",
                "bpl",
                "bvs",
                "bvc",
                "bhi",
                "bls",
                "bge",
                "blt",
                "bgt",
                "ble",
                "bal",
                "bnv",

                "cbnz",
                "cbz",
                "tbnz",
                "tbz",

                "blraa",
                "blraaz",
                "blrab",
                "blrabz",

                "braa",
                "braaz",
                "brab",
                "brabz",
            };

            private static readonly string[] JumpInstructions = new string[]
            {
                "b",
                "br",
                "blr",
                "bx",
                "bl",
                "blx",
                "bxj",
            };

            private static readonly string[] ReturnInstructions = new string[]
            {
                "ret",
                "retaa",
                "retab",
            };

            private static readonly string[] SimdInstructions = new string[]
            {
                // TODO: add missing instructions
                "vld1",
                "vld2",
                "vld3",
                "vld4",
                "vabs",
                "vadd",
                "vaddl",
                "vaddw",
                "vmov",
                "movs",
                "vmul",
                "vsub",
                "vaba",
                "vabl",
                "vabd",
                "vabdl",
                "vacge",
                "vacgt",
                "vacle",
                "vaclt",
                "vaddhn",
                "vand",
                "vbic",
                "vbif",
                "vbit",
                "vbsl",
                "vceq",
                "vcge",
                "vcgt",
                "vcle",
                "vcls",
                "vcnt",
                "vclt",
                "vclz",
                "vcvt",
                "vdup",
                "veor",
                "vext",
                "vfma",
                "vfms",
                "vhadd",
                "vhsub",
                "vld",
                "vmax",
                "vmin",
                "vmla",
                "vmls",
                "vmovl",
                "vmovn",
                "vmvn",
                "vneg",
                "vorn",
                "vorr",
                "vpadal",
                "vpadd",
                "vpmax",
                "vpmin",
                "vqabs",
                "vqadd",
                "vqdmlal",
                "vqdmlsl",
                "vqdmull",
                "vqdmulh",
                "vqmovn",
                "vqneg",
                "vqrdmulh",
                "vqrshl",
                "vqrshrn",
                "vqshl",
                "vqshrn",
                "vqsub",
                "vraddhn",
                "vrecpe",
                "vrecps",
                "vrev",
                "vrev16",
                "vrev32",
                "vrev64",
                "vrhadd",
                "vrshr",
                "vrshrn",
                "vrsqrte",
                "vrsqrts",
                "vrsra",
                "vrsubhn",
                "vshl",
                "vshr",
                "vshrn",
                "vsli",
                "vsra",
                "vsri",
                "vst",
                "vst1",
                "vst2",
                "vst3",
                "vst4",
                "vsubhn",
                "vswp",
                "vtbl",
                "vtbx",
                "vtrn",
                "vtst",
                "vuzp",
                "vzip",
                "vldm",
                "vldmia",
                "vldmdb",
                "vldr",
                "vmrs",
                "vmsr",
                "vpop",
                "vpush",
                "vstm",
                "vstmia",
                "vstr",
                "vcmp",
                "vcmpe",
                "vcvtb",
                "vcvtt",
                "vdiv",
                "vfnma",
                "vfnms",
                "vnmla",
                "vnmls",
                "vnmul",
                "vsqrt",
                "push",
                "pop",
                "pkhbt",
                "pkhtb",
            };

            private ARM64AsmTokenKindProvider() : base(Registers.Length +
                                                       Instructions.Length +
                                                       BranchInstructions.Length +
                                                       JumpInstructions.Length +
                                                       ReturnInstructions.Length +
                                                       SimdInstructions.Length)
            {
                foreach (var register in Registers)
                {
                    AddTokenKind(register, AsmTokenKind.Register);
                }

                foreach (var instruction in Instructions)
                {
                    AddTokenKind(instruction, AsmTokenKind.Instruction);
                }

                foreach (var instruction in BranchInstructions)
                {
                    AddTokenKind(instruction, AsmTokenKind.BranchInstruction);
                }

                foreach (var instruction in JumpInstructions)
                {
                    AddTokenKind(instruction, AsmTokenKind.JumpInstruction);
                }

                foreach (var instruction in ReturnInstructions)
                {
                    AddTokenKind(instruction, AsmTokenKind.ReturnInstruction);
                }

                foreach (var instruction in SimdInstructions)
                {
                    AddTokenKind(instruction, AsmTokenKind.InstructionSIMD);
                }
            }

            public override AsmTokenKind FindTokenKind(StringSlice instr)
            {
                instr = TryRemoveT(instr); // error here now that I added b.le etc.

                var kind = base.FindTokenKind(instr);
                if (kind != AsmTokenKind.Identifier)
                {
                    return kind;
                }

                // Did not find instruction in listed instructions.
                // Try matching whether it ended on a condition flag.
                instr = TryRemoveCond(instr);
                kind = base.FindTokenKind(instr);
                return kind;
            }

            private static bool IsConditional(string text)
            {
                switch (text)
                {
                    case "eq":
                    case "ne":
                    case "cs":
                    case "hs":
                    case "cc":
                    case "lo":
                    case "mi":
                    case "pl":
                    case "vs":
                    case "vc":
                    case "hi":
                    case "ls":
                    case "ge":
                    case "lt":
                    case "gt":
                    case "le":
                    case "al":
                        return true;
                    default:
                        return false;
                }
            }


            internal static StringSlice TryRemoveT(StringSlice instr)
            {
                var idx = instr.IndexOf('.');
                if (idx < 0)
                {
                    return instr;
                }

                var instrS = instr.ToString();
                if (IsConditional(instrS.Substring(idx+1)))
                {
                    // remove '.' from string e.g. b.le => ble
                    return new StringSlice(instrS.Remove(idx, 1));
                }

                return new StringSlice(instrS, 0, idx);
            }

            internal static StringSlice TryRemoveCond(StringSlice instr)
            {
                if (instr.Length < 2)
                {
                    return instr;
                }

                var instrS = instr.ToString();
                var idx = instr.Length - 2;

                return IsConditional(instrS.Substring(idx, 2))
                    ? new StringSlice(instrS, 0, idx)
                    : instr;
            }

            private static bool IsFloatingRegister(string reg)
            {
                if (reg == "sp")
                {
                    return false;
                }
                var c = reg[0];
                return c == 'b' || c == 'h' || c == 's' || c == 'd' || c == 'q' || c == 'v';
            }

            private static int StripSizeIdentifier(string reg, int idx)
            {
                // "sp" only register that defies the heuristic.
                if (reg != "sp")
                {
                    switch (reg[idx])
                    {
                        case 'w':
                        case 'x':
                        case 's':
                        case 'q':
                        case 'h':
                        case 'b':
                        case 'd':
                        case 'v':
                            idx++;
                            break;
                    }
                }

                return idx;
            }

            public override bool RegisterEqual(string regA, string regB)
            {
                var isAFloat = IsFloatingRegister(regA);
                var isBFloat = IsFloatingRegister(regB);

                if ((isAFloat && isBFloat) || (isAFloat == isBFloat))
                {
                    var idxA = StripSizeIdentifier(regA, 0);
                    var idxB = StripSizeIdentifier(regB, 0);

                    var lenA = regA.Length;
                    var lenB = regB.Length;

                    while (idxA < lenA && idxB < lenB)
                    {
                        if (regA[idxA] != regB[idxB])
                        {
                            break;
                        }
                        if ((idxA == lenA - 1 || regA[idxA+1] == '.')
                            && (idxB == lenB - 1 || regB[idxB+1] == '.'))
                        {
                            return true;
                        }
                        idxA++;
                        idxB++;
                    }
                }

                return false;
            }

            /// <summary>
            /// Assumes <see cref="instruction"/> is an arm SIMD instruction.
            /// Returns whether it's a packed or scalar SIMD instruction.
            /// </summary>
            public override SIMDkind SimdKind(StringSlice instruction) =>
                instruction.Contains('.')
                    ? SIMDkind.Packed
                    : SIMDkind.Scalar;

            public static readonly ARM64AsmTokenKindProvider Instance = new ARM64AsmTokenKindProvider();
        }
    }
}
#endif